package com.easychat.service.impl;

import com.easychat.constants.Constants;
import com.easychat.entity.config.AppConfig;
import com.easychat.entity.dto.MessageSendDTO;
import com.easychat.entity.dto.SysSettingDTO;
import com.easychat.entity.dto.TokenUserInfoDTO;
import com.easychat.entity.enums.MessageStatusEnum;
import com.easychat.entity.enums.MessageTypeEnum;
import com.easychat.entity.enums.ResponseCodeEnum;
import com.easychat.entity.enums.UserTypeEnum;
import com.easychat.entity.po.ChatMessage;
import com.easychat.entity.po.ChatSession;
import com.easychat.entity.po.UserContact;
import com.easychat.entity.query.ChatMessageQuery;
import com.easychat.entity.query.ChatSessionQuery;
import com.easychat.entity.query.UserContactQuery;
import com.easychat.exception.BusinessException;
import com.easychat.mappers.ChatMessageMapper;
import com.easychat.mappers.ChatSessionMapper;
import com.easychat.mappers.UserContactMapper;
import com.easychat.redis.RedisComponent;
import com.easychat.service.AsyncService;
import com.easychat.service.ChatMessageService;
import com.easychat.utils.StringTools;
import com.easychat.websocket.MessageHandler;
import io.github.lnyocly.ai4j.platform.openai.chat.entity.ChatCompletion;
import io.github.lnyocly.ai4j.platform.openai.chat.entity.ChatCompletionResponse;
import io.github.lnyocly.ai4j.service.IChatService;
import io.github.lnyocly.ai4j.service.PlatformType;
import io.github.lnyocly.ai4j.service.factor.AiService;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import javax.annotation.PreDestroy;
import javax.annotation.Resource;
import java.util.List;

@Service
public class AsyncServiceImpl implements AsyncService {

    private static final Logger logger = LoggerFactory.getLogger(AsyncServiceImpl.class);

    // 注入Ai服务
    @Autowired
    private AiService aiService;
//    // 发送GET请求
//    @Resource
//    private RestTemplate restTemplate;
    @Resource
    private ChatMessageMapper<ChatMessage, ChatMessageQuery> chatMessageMapper;
    @Resource
    private ChatSessionMapper<ChatSession, ChatSessionQuery> chatSessionMapper;
    @Resource
    private UserContactMapper<UserContact, UserContactQuery> userContactMapper;
    @Resource
    private RedisComponent redisComponent;
    @Resource
    private MessageHandler messageHandler;


    /**
     * 执行异步任务，将question发送给AI模型，
     * 待结果返回后，再发给提问的用户
     * @param question 发送给AI服务的问题
     * @param contactId 接收机器人消息的用户id
     */
    @Override
    @Async(value = "asyncServiceExecutor")//使用自定义的线程池
    public void executeAsync(String question, String contactId) {
        logger.info("异步任务开始...");
        //****************************向另一台服务器发请求，间接调用AI************************************
        /*
        // 向AI聊天服务发送GET请求
        String url = "http://localhost:8081/ai/chat?question=" + question;

        // 使用getForEntity方法发送GET请求并获取响应实体
        ResponseEntity<String> responseEntity = restTemplate.getForEntity(url, String.class);
        System.out.println("Status Code: " + responseEntity.getStatusCode());
        System.out.println("Response Body: " + responseEntity.getBody());
        String content = responseEntity.getBody();
        */
        //***********************************直接调用AI**************************************************
        //获取ZhiPu AI的聊天服务
        IChatService chatService = aiService.getChatService(PlatformType.ZHIPU);//Zhipu(智谱)平台

        // 创建请求参数
        ChatCompletion chatCompletion = ChatCompletion.builder()
                .model("glm-4-flash")//语言模型
                .message(io.github.lnyocly.ai4j.platform.openai.chat.entity.ChatMessage.withUser(question))
                .build();
        logger.info(chatCompletion.toString());
        // 发送chat请求
        ChatCompletionResponse chatCompletionResponse = null;
        try {
            chatCompletionResponse = chatService.chatCompletion(chatCompletion);
        } catch (Exception e) {
            throw new BusinessException("调用AI接口发生错误");
        }
        // 获取聊天内容和token消耗
        String content = chatCompletionResponse.getChoices().get(0).getMessage().getContent().getText();
        long totalTokens = chatCompletionResponse.getUsage().getTotalTokens();
        logger.info("总token消耗: " + totalTokens);
        //TODO  将聊天返回结果发送给用户
        //获取系统设置
        SysSettingDTO sysSetting = redisComponent.getSysSetting();
        //获取机器人信息
        String robotUid = sysSetting.getRobotUid();
        TokenUserInfoDTO robotToken = new TokenUserInfoDTO();
        robotToken.setUserId(robotUid);
        robotToken.setNickName(sysSetting.getRobotNickName());
        //构建聊天消息(机器人发送)
        com.easychat.entity.po.ChatMessage robotChatMessage = new com.easychat.entity.po.ChatMessage();
        robotChatMessage.setContactId(contactId);
        //设置AI返回的消息内容
        robotChatMessage.setMessageContent(content);
        robotChatMessage.setMessageType(MessageTypeEnum.CHAT.getType());
        this.saveMessage(robotChatMessage, robotToken);//机器人发消息给人类
        logger.info("异步任务结束...");
    }


    /**
     * 保存聊天消息
     * @param chatMessage 聊天消息
     * @param tokenUserInfoDTO 发送人token信息
     * @return
     */
    public MessageSendDTO saveMessage(ChatMessage chatMessage, TokenUserInfoDTO tokenUserInfoDTO) {
        //更新chat_message表
        String sessionId = null;
        String sendUserId = tokenUserInfoDTO.getUserId();//发送人id
        String contactId = chatMessage.getContactId();//联系人id
        //sendUserId sendUserNickName
        chatMessage.setSendUserId(sendUserId);
        chatMessage.setSendUserNickName(tokenUserInfoDTO.getNickName());
        //contactId
        chatMessage.setContactId(contactId);
        //sendTime
        Long curTime = System.currentTimeMillis();
        chatMessage.setSendTime(curTime);
        //sessionId
        UserTypeEnum contactTypeEnum = UserTypeEnum.getByPrefix(contactId);//联系人类型
        if (UserTypeEnum.USER == contactTypeEnum) {//根据联系人类型获取sessionId
            sessionId = StringTools.getChatSessionId4User(new String[]{contactId, sendUserId});
        } else {
            sessionId = StringTools.getChatSessionId4Group(contactId);
        }
        chatMessage.setSessionId(sessionId);
        //contactType
        chatMessage.setContactType(contactTypeEnum.getType());
        //messageType
        MessageTypeEnum messageTypeEnum = MessageTypeEnum.getByType(chatMessage.getMessageType());//消息类型
        if (messageTypeEnum == null || !(MessageTypeEnum.CHAT == messageTypeEnum || MessageTypeEnum.MEDIA_CHAT == messageTypeEnum)) {
            //无法获取消息类型 / 消息类型不是聊天或者媒体文件
            throw new BusinessException(ResponseCodeEnum.CODE_600);
        }
        //status
        Integer status = null;//发送状态
        if (MessageTypeEnum.MEDIA_CHAT == messageTypeEnum) {
            status = MessageStatusEnum.LOADING.getStatus();//媒体文件初始状态：发送中
        } else {
            status = MessageStatusEnum.COMPLETED.getStatus();//普通聊天信息状态：已发送
        }
        chatMessage.setStatus(status);
        //messageContent
        String messageContent = StringTools.cleanHtmlTag(chatMessage.getMessageContent());//防止注入
        chatMessage.setMessageContent(messageContent);
        chatMessageMapper.insert(chatMessage);

        //更新会话chat_session
        ChatSession chatSession = new ChatSession();
        chatSession.setLastMessage(chatMessage.getMessageContent());
        if (UserTypeEnum.GROUP == contactTypeEnum) {
            //联系人为群组，最后的消息显示为：发送人昵称：说的话
            chatSession.setLastMessage(tokenUserInfoDTO.getNickName() + "：" + chatMessage.getMessageContent());
        }
        chatSession.setLastReceiveTime(curTime);
        chatSessionMapper.updateBySessionId(chatSession, sessionId);//聊天时，会话已经存在，所以是更新而不是插入

        //发消息
        MessageSendDTO messageSendDTO = new MessageSendDTO();
        BeanUtils.copyProperties(chatMessage, messageSendDTO);
        messageHandler.sendMessage(messageSendDTO);

        return messageSendDTO;
    }
}


